package org.wattdepot.gdata.kukuicup.updater.energygoalgame;

import java.net.URL;
import java.util.Calendar;
import java.util.List;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.gdata.kukuicup.service.GDataBatchRequest;
import org.wattdepot.gdata.kukuicup.service.GDataService;
import org.wattdepot.gdata.kukuicup.service.WattDepotService;
import org.wattdepot.gdata.kukuicup.updater.BaselineView;
import org.wattdepot.gdata.kukuicup.updater.GDataTask;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Implements the task to obtain the goal and actual consumption for the given source to date 
 * for today.
 * @author Philip Johnson
 */
public class EnergyGoalGameTask extends GDataTask {
  
  /** Holds the read-only view of the baseline data. */
  private BaselineView baselineView;

  /** The goal reduction we will use during this run. */
  private int goalReduction;
  
  /** The warning reduction we will use during this run. */
  private int warningReduction;
    
  /**
   * Creates the task that gets woken up regularly (typically every 15 minutes). 
   * @param wattDepotService The WattDepot service configuration data. 
   * @param gdataService The gdata service configuration data.
   * @param spreadsheetURL The URL to the spreadsheet containing the data.
   * @param sourceName The WattDepot source of interest.
   * @param isVirtual True if the source is virtual.
   * @param row The row in the spreadsheet that this source's data appears in.
   * @param goalReduction The percentage reduction to meet the goal.
   * @param warningReduction  The percentage reduction to get to green.
   */
  public EnergyGoalGameTask(WattDepotService wattDepotService, GDataService gdataService, 
      URL spreadsheetURL, String sourceName, boolean isVirtual, int row, int goalReduction,
      int warningReduction) {
    super(wattDepotService, gdataService, spreadsheetURL, sourceName, row, "EnergyGoalGame",
        isVirtual);
    this.baselineView = new BaselineView(gdataService);
    this.goalReduction = goalReduction;
    this.warningReduction = warningReduction;
  }
  
  /**
   * Return the baseline level of consumption for this source at this moment in time. 
   * @return The baseline level of consumption.
   */
  public int getBaselineConsumption() {
    XMLGregorianCalendar now = Tstamp.makeTimestamp();
    // Get the 24 baseline values for this day.
    int dayOfWeek = now.toGregorianCalendar().get(Calendar.DAY_OF_WEEK);
    List<Integer> baselines = baselineView.getHourlyBaselines(sourceName, dayOfWeek);
    // Now figure out the baseline consumption from midnight until the beginning of this hour.
    int hourOfDay = now.toGregorianCalendar().get(Calendar.HOUR_OF_DAY);
    double baselineConsumption = 0;
    for (int i = 0; i < hourOfDay; i++) {
      baselineConsumption += baselines.get(i);
    }
    // Add in the fractional amount for this portion of the hour.
    double minutes = now.toGregorianCalendar().get(Calendar.MINUTE);
    double currentHourBaseline = baselines.get(hourOfDay);
    double thisHourBaseline = (currentHourBaseline * (minutes / 60.0));
    baselineConsumption += thisHourBaseline;
    return (int) (baselineConsumption / 1000);
  }
  
  /**
   * Return the goal consumption given the baseline.  
   * @param baselineConsumption The baseline consumption.
   * @return The goal consumption.
   */
  public int getGoalConsumption(int baselineConsumption) {
    int goalConsumption = 
      (int) (baselineConsumption - (baselineConsumption * (goalReduction / 100.0))); 
    // Compute and return the goal by subtracting the percentage reduction from the baseline.
    return goalConsumption;
  }
  
  /**
   * Return the warning consumption given the baseline.  
   * @param baselineConsumption The baseline consumption.
   * @return The warning consumption.
   */
  public int getWarningConsumption(int baselineConsumption) {
    int warningConsumption = 
      (int) (baselineConsumption - (baselineConsumption * (warningReduction / 100.0))); 
    // Compute and return the goal by subtracting the percentage reduction from the baseline.
    return warningConsumption;
  }
  
  /**
   * Return the actual consumption from the start of today until now.
   * @return The actual consumption to date for today.
   * @throws Exception if problems occur getting actual consumption
   */
  public int getActualConsumption() throws Exception {
    XMLGregorianCalendar startOfDay = Tstamp.makeTimestamp();
    startOfDay.setHour(0);
    startOfDay.setMinute(0);
    startOfDay.setSecond(0);
    startOfDay.setMillisecond(0);
    XMLGregorianCalendar now = Tstamp.makeTimestamp();

    int kWh = 0;
    WattDepotClient client = this.wattDepotService.getClient();
    // Look back 1 minute to make sure we have sensor data. 
    // This introduces a slight inaccuracy in our baseline vs. actual. 
    XMLGregorianCalendar justNow = Tstamp.incrementMinutes(now, -1);
    double energy = client.getEnergyConsumed(sourceName, startOfDay, justNow, 10);
    kWh = (int) (energy / 1000); 
    return kWh;
  }

  /**
   * Retrieve the baseline consumption data from the Baselines spreadsheet, retrieve the current
   * power consumed from WattDepot, and write out the current state. 
   */
  @Override
  public void run() {
    try {
      int baselineConsumption = getBaselineConsumption();
      int goalConsumption = getGoalConsumption(baselineConsumption);
      int warningConsumption = getWarningConsumption(baselineConsumption);

      startWattDepotTiming();
      int actualConsumption = getActualConsumption();
      stopWattDepotTiming();

      // Return immediately if actual consumption is not positive.
      if (actualConsumption <= 0) {
        printTaskResults("EnergyGoalGame", "actual<=0", String.valueOf(actualConsumption));
        return;
      }
      
      // Now update the spreadsheet.
      startGDataTiming();
      GDataBatchRequest request = new GDataBatchRequest(gdataService, spreadsheetURL);
      // Update the lastUpdate cell, the actual cell, and the goal cell.
      request.addRequest(this.row, 2, gdataService.makeLastUpdate());
      request.addRequest(this.row, 3, String.valueOf(actualConsumption));
      request.addRequest(this.row, 4, String.valueOf(goalConsumption));
      request.addRequest(this.row, 5, String.valueOf(warningConsumption));
      request.sendRequest();
      stopGDataTiming();
      printTaskResults(String.valueOf(actualConsumption), 
          String.valueOf(goalConsumption), "actual/goal");
    }
    catch (Exception e) {
      printTaskResults("Exception in EnergyGoalGameTask","","");
      e.printStackTrace();
    }
  }
}
